package com.xzc.seckill.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xzc.seckill.mapper.GoodsMapper;
import com.xzc.seckill.mapper.OrderMapper;
import com.xzc.seckill.pojo.*;
import com.xzc.seckill.service.GoodsService;
import com.xzc.seckill.service.OrderService;
import com.xzc.seckill.service.SeckillGoodsService;
import com.xzc.seckill.service.SeckillOrderService;
import com.xzc.seckill.util.MD5Util;
import com.xzc.seckill.util.UUIDUtil;
import com.xzc.seckill.vo.GoodsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author 徐生
 * @version 1.0
 * Date 2024/4/6 22:03
 * Description: OrderServiceImpl
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    //装配需要的组件或对象
    @Autowired
    private SeckillGoodsService seckillGoodsService;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private SeckillOrderService seckillOrderService;
    @Autowired
    private RedisTemplate redisTemplate;

    //完成秒杀
    @Transactional
    public Order seckill(User user, GoodsVo goodsVo) {
        //根据商品id 查询秒杀商品的库存，并-1
        SeckillGoods seckillGoods = seckillGoodsService.getOne(new QueryWrapper<SeckillGoods>().eq("goods_id", goodsVo.getId()));

        //完成一个基本的秒杀操作[这里不具有原子性]，在高并发情况下还会优化
//        seckillGoods.setStockCount(seckillGoods.getStockCount() - 1);
//        seckillGoodsService.updateById(seckillGoods);

        //业务分析
        //1. Mysql在默认的事务隔离级别下[REPEATABLE-READ]
        //2. 执行update语句时，会在事务中锁定要更新的行（使线程一个一个的执行）
        //3. 这样可以防止其他会话在同一执行update，delete
        // 在更新成功时才返回true，否则为false，即更新的行数大于1才为true
        boolean update = seckillGoodsService.update(new UpdateWrapper<SeckillGoods>().setSql("stock_count=stock_count-1")
                .eq("goods_id", goodsVo.getId())
                .gt("stock_count", 0));//gt()：stock_count大于0        ge：大于等于
        if (!update) {//如果更新失败，说明没有库存了
            return null;//为了防止同一时间生成多余的订单
        }

        //生成普通订单
        Order order = new Order();
        order.setUserId(user.getId());
        order.setGoodsId(goodsVo.getId());
        order.setDeliveryAddrId(0L);    //设置一个初始值
        order.setGoodsName(goodsVo.getGoodsName());
        order.setGoodsCount(1);
        order.setGoodsPrice(seckillGoods.getSeckillPrice());
        order.setOrderChannel(1);       //设置一个初始值
        order.setStatus(0);             //未支付状态
        order.setCreateDate(new Date());//设置当前时间
        orderMapper.insert(order);      //保存order信息

        //生成秒杀订单
        SeckillOrder seckillOrder = new SeckillOrder();
        seckillOrder.setGoodsId(goodsVo.getId());
        //这里秒杀商品订单对应的order_id 是从上面添加order后获取到的
        seckillOrder.setOrderId(order.getId());
        seckillOrder.setUserId(user.getId());
        seckillOrderService.save(seckillOrder);//保存seckillOrder信息

        //将生成的秒杀订单，存入redis，这样在查询某个用户是否已经秒杀了这个商品时，直接到redis中查询，起到优化效果
        //设计秒杀订单的key => order:用户id:商品id
        redisTemplate.opsForValue().set("order:"+user.getId()+":"+goodsVo.getId(),seckillOrder);
        return order;
    }


    //生成秒杀路径/值(唯一)
    public String createPath(User user, Long goodsId) {
        //生成秒杀路径/值
        String path = MD5Util.md5(UUIDUtil.uuid());
        //将随机生成的路径保存到redis，设置一个超时时间60s
        //key的设计：seckillPath:userId:goodsId
        redisTemplate.opsForValue().set("seckillPath:" + user.getId() + ":" + goodsId, path, 60, TimeUnit.SECONDS);
        return path;
    }

    //对秒杀路径进行校验
    public boolean checkPath(User user, Long goodsId, String path) {
        if (user==null || goodsId<0 || !StringUtils.hasText(path)) return false;
        //取出该用户秒杀该商品的路径
        String redisPath = (String)redisTemplate.opsForValue().get("seckillPath:" + user.getId() + ":" + goodsId);
        return path.equals(redisPath);
    }


    //验证用户输入的验证码是否正确
    public boolean checkCaptcha(User user, Long goodsId, String captcha) {
        if (user==null || goodsId<0 || !StringUtils.hasText(captcha)) return false;
        //从redis取出验证码
        String redisCaptcha = (String) redisTemplate.opsForValue().get("captcha:" + user.getId() + ":" + goodsId);
        return captcha.equals(redisCaptcha);
    }
}
